#include "typedef.h"
#include "uart2.h"
#include "stm32f10x.h"
#include "print.h"
#include "led.h"
#include <stdio.h>
#include "systick.h"
#include "cc9d.h"
#include "adc.h"

static void ledControlReceivePkg(sCc9dPkg* pkg, uint8 data_len);
static void adcReportGetPkg(sCc9dPkg* pkg, uint8 data_len);
/***********************************************
 * MCU使用CC9D和斥候无线模块联系，CC9D是一个简单的串口协议，可以通过串口发送和接收数据。
 * 要使用CC9D，需要添加两个c文件：
 *     cc9d.c cc9d.h
 * 包含cc9d.h头文件，此外还要实现以下两个函数：（在CC9D中，需要模块外部实现的接口函数以“_”开头）
 * 1.  void _cc9d_sendByte(uint8 b);                                 
 *    需要发送1byte时，会调用此函数。
 * 2.  sCc9dPkg* _cc9d_receivePkg(sCc9dPkg* pkg, uint8 data_len);    
 *    收到一个包时，会调用此函数。
 * 还要在使用时调用两个函数：
 * 3.cc9d_recvbyte(byte b);  
 *    外部程序收到1Byte数据时，调用此函数将数据交给解码器。
 * 4.cc9d_SendPkg(sCc9dPkg* pkg, uint8 data_len);  
 *    命令cc9d发送一个包，需要通过无线发送数据的时候都需要使用此函数。
***********************************************/

//1. 需要发送1byte时，会调用此函数。
void _cc9d_sendByte(uint8 b) {
  Uart2_sendByte(b);//将数据通过uart2发出
}

//2. 收到一个包时，会调用此函数。
sCc9dPkg* _cc9d_receivePkg(sCc9dPkg* pkg, uint8 data_len) {
  /***********************************************
   * 一般情况下，收到包后需要根据目的端口号来决定如何处理数据
   * 在程序模型中，一个嵌入式程序可能包含多个组件，
   * 例如LED组件、电机组件、电池电量采集组件等
   * 可以为每个需要使用无线通信的组件模块分配一个端口，
   * 收到包后，根据目的端口号，将包交给特定的组件处理。
   **************************************************/
  switch (pkg->to_port) {
  case 16://将16号端口分配给LED，因此，从此处收到的数据需要发给
    ledControlReceivePkg(pkg, data_len);
    break;
  case 32:
    adcReportGetPkg(pkg, data_len);
    break;
  default://应该避免给模块发送目的端口号没有使用的包。
    printf("Reserved port %d get a pkg, from %X.%d\n\tdatas: ", pkg->to_port, pkg->remote_addr, pkg->from_port);
		for(int i=0;i<data_len;i++){			
      printf("%02x ", pkg->datas[i]);
    }
    printf("\n");
    break;
  }
  //返回pkg，意味着在退出此函数后CC9D继续使用pkg接收下一个包。
  //如果程序中还要继续使用pkg中的数据，则必须准备一个未用的包指针，以供CC9D接收数据。
  return pkg;
}

//3. 此函数会不断检查串口接收缓冲，如果收到了数据，就使用cc9d_recvbyte()函数传递给CC9D。
static void checkUart2Recv() {
  int16 data;
  data = Uart2_recvByte();
  while (data!=-1){
    cc9d_recvbyte(data);
    data = Uart2_recvByte();
  }
}


//LED控制函数。占用16端口，在收到对应的命令后，控制LED的亮灭。
#define CMD_RED 1 
#define CMD_YELLOW 2
#define CMD_GREEN 3
//如果收到其他命令，则关闭全部LED。
void ledControlReceivePkg(sCc9dPkg* pkg, uint8 data_len) {
  if (pkg->datas[0] == CMD_RED) {
    redOn();
    yellowOff();
    greenOff();
  } else if (pkg->datas[0] == CMD_YELLOW) {
    redOff();    
    yellowOn();
    greenOff();
  } else if (pkg->datas[0] == CMD_GREEN) {
    redOff();
    yellowOff();
    greenOn();
  } else {
    redOff();
    yellowOff();
    greenOff();
  }
}

//LED闪烁相关函数
volatile int on_ms = 1000;
volatile int off_ms = 1000;
//LED闪烁函数，在主函数循环中，每隔1ms调用一次，用于执行固定时间间隔的任务
void ledTwinkle_ms() {
  static uint32 ms_ticks = 0;
  ms_ticks++;
  if (ms_ticks == on_ms) {
    blueOn();
  } else if (ms_ticks >= on_ms + off_ms) {
    blueOff();
    ms_ticks = 0;
  }
}

static int32 last_adc_value = 0;//上一次的ADC值
static int32 adc_report_threshold = 15;//ADC值变化的阈值，超过此值则发送一个包报告ADC值。
static uint16 hart_beat_ticks = 10000;//每100ms进行一个计数，到100时发送一个包，并重新计数。
static uint16 hart_beat_interval = 100;//心跳包的最大计数，10s发送一个心跳包。
static void adcReportGetPkg(sCc9dPkg* pkg, uint8 data_len) {
  if(data_len == 0){
    return;
  }
  if(pkg->datas[0] == 'r'){//发送r立即读取一个ADC值
    hart_beat_ticks = 10000;
  }else if(pkg->datas[0] == 'i'){//发送i后面跟随ADC采样间隔，设置心跳包的时间间隔
    if(data_len != 3){
      printf("ADC report interval set error, data_len = %d\n", data_len);
      return;
    }
    uint16 interval = pkg->datas[2];
    interval<<=8;
    interval += pkg->datas[1];
    printf("ADC report interval set to (%d x 100) ms\n", interval);
    hart_beat_interval = interval;
  }
  else if (pkg->datas[0] == 't') {//发送t后面跟随ADC阈值，设置ADC报告的阈值
    if(data_len != 2){
      printf("ADC report threshold set error, data_len = %d\n", data_len);
      return;
    }
    adc_report_threshold = pkg->datas[1];
    if(adc_report_threshold < 5){
      adc_report_threshold = 5;
    }
    printf("ADC report threshold set to %d\n", adc_report_threshold);
  }
}

//ADC报告函数，占用32端口，用于发送ADC采集到的数据。
static void sendAdcPkg(int32 adc_value){
  static uint16 sno = 0;
  sCc9dPkg send_pkg;
  send_pkg.from_port = 32;
  send_pkg.to_port = 32;
  send_pkg.remote_addr = 0x0000;
  send_pkg.datas[0] = (adc_value >> 0) & 0xff;
  send_pkg.datas[1] = (adc_value >> 8) & 0xff;
  send_pkg.datas[2] = (sno >> 0) & 0xff;
  send_pkg.datas[3] = (sno >> 8) & 0xff;
  cc9d_SendPkg(&send_pkg, 4);
  printf("GPIO A0 voltage: %f\n", Adc_getVoltage((uint16)adc_value));
  sno++;
}

//Adc循环读取函数，每1ms被调用一次，每100ms读取一次ADC值。
//如果ADC值发生了大于15的变化，则发送一个包，报告ADC值。
//如果ADC值一直没有变化，则每10s发送一个包，报告ADC值。同时作为心跳包。
void adcReport_ms() {
  static uint32 ms_ticks = 0;//1ms计数
  ms_ticks++;
  if (ms_ticks % 100 == 0) {
    int32 adc_value = Adc_getValue(0);
    int32 diff = adc_value - last_adc_value;
    if ((diff > adc_report_threshold) || (diff < -adc_report_threshold) || (hart_beat_ticks >= hart_beat_interval)) {
      sendAdcPkg(adc_value);
      last_adc_value = adc_value;
      hart_beat_ticks = 0;
    }else{
      hart_beat_ticks++;
    }
  }
}




sCc9dPkg recv_pkg_space;
int main(void) {
  //初始化各种模块
  Led_init();
  Adc_init();
  Print_init(115200);
  Uart2_init();
  Systick_init(1000);
  cc9d_init(&recv_pkg_space);
  printf("\n\n ****  chhol adc report V1.2 ****\n");
  //进入主循环
  /**************************************************
   * Systick_checkEventCount()函数来自系统定时器。
   * 每当定时器计时1ms，调用时就会返回1，否则返回0。
   * 当1ms到来时，调用各种需要周期性执行的函数。
   * 在其余空闲的时间，则执行其他对时间不敏感的轮询的任务。 
   */
  while (1) {
    if (0 != Systick_checkEventCount()) {//检查是否到了1ms。
      ledTwinkle_ms();
      adcReport_ms();
    }
    //从串口2的缓冲区中读取数据，交给CC9D解码器处理。
    checkUart2Recv();
  }
}


